import tkinter as tk
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
import subprocess
import sys
import threading
import os
import webbrowser
import shutil
from tkinter import messagebox


class PipManager(ttk.Window):
    def __init__(self):
        super().__init__(themename="superhero")
        self.title("Pip 包管理器")
        self.geometry("900x700")
        self.resizable(True, True)
        self.python_executable = None  # 存储Python解释器路径
        self.create_widgets()
        self.center_window()
        self.check_python_environment()

        # 确保UI初始化完成
        self.update_idletasks()
        self.after(100, self.finalize_ui)

    def finalize_ui(self):
        """确保所有UI元素正确初始化"""
        # 确保状态标签可见
        self.status_label.config(text="就绪")
        # 确保清除按钮可见
        self.clear_btn.config(state=NORMAL)
        # 初始隐藏进度条
        self.hide_progress_bar()
        self.update_idletasks()

    def center_window(self):
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.winfo_screenwidth() // 2) - (width // 2)
        y = (self.winfo_screenheight() // 2) - (height // 2)
        self.geometry(f"{width}x{height}+{x}+{y}")

    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self, padding=15)
        main_frame.pack(fill=BOTH, expand=YES)

        # 标题
        title_frame = ttk.Frame(main_frame)
        title_frame.pack(fill=X, pady=(0, 15))

        title_label = ttk.Label(
            title_frame,
            text="Python Pip 包管理器",
            font=("Helvetica", 18, "bold"),
            bootstyle="inverse-primary"
        )
        title_label.pack(pady=5, fill=X)

        # 环境状态显示
        self.env_frame = ttk.LabelFrame(main_frame, text="Python 环境状态", padding=10)
        self.env_frame.pack(fill=X, pady=(0, 15))

        env_inner_frame = ttk.Frame(self.env_frame)
        env_inner_frame.pack(fill=X)

        self.python_status_label = ttk.Label(
            env_inner_frame,
            text="正在检测Python环境...",
            font=("Helvetica", 10),
            bootstyle="warning"
        )
        self.python_status_label.pack(side=LEFT, padx=(0, 10), fill=X, expand=True)

        self.download_python_btn = ttk.Button(
            env_inner_frame,
            text="下载 Python",
            command=self.download_python,
            bootstyle="light",
            width=15,
            state=DISABLED
        )
        self.download_python_btn.pack(side=RIGHT)

        # 操作面板
        control_frame = ttk.LabelFrame(main_frame, text="包管理操作", padding=10)
        control_frame.pack(fill=X, pady=(0, 15))

        # 输入区域
        input_frame = ttk.Frame(control_frame)
        input_frame.pack(fill=X, pady=5)

        input_label = ttk.Label(
            input_frame,
            text="输入包名称（多个包用空格分隔）:",
            font=("Helvetica", 10)
        )
        input_label.pack(side=LEFT, padx=(0, 10))

        self.package_entry = ttk.Entry(input_frame, width=50)
        self.package_entry.pack(side=LEFT, fill=X, expand=YES, padx=(0, 10))

        # 按钮区域 - 使用网格布局并均匀分配空间
        button_frame = ttk.Frame(control_frame)
        button_frame.pack(fill=X, pady=10)

        # 配置网格列均匀分布
        for i in range(3):
            button_frame.columnconfigure(i, weight=1, uniform="button_cols")

        # 第一行按钮
        self.install_btn = ttk.Button(
            button_frame,
            text="安装包",
            command=self.install_package,
            bootstyle=SUCCESS,
            width=15
        )
        self.install_btn.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")

        self.uninstall_btn = ttk.Button(
            button_frame,
            text="卸载包",
            command=self.uninstall_package,
            bootstyle=DANGER,
            width=15
        )
        self.uninstall_btn.grid(row=0, column=1, padx=5, pady=5, sticky="nsew")

        self.update_pip_btn = ttk.Button(
            button_frame,
            text="更新 Pip",
            command=self.update_pip,
            bootstyle=INFO,
            width=15
        )
        self.update_pip_btn.grid(row=0, column=2, padx=5, pady=5, sticky="nsew")

        # 第二行按钮
        self.update_pkg_btn = ttk.Button(
            button_frame,
            text="更新已安装包",
            command=self.update_package,
            bootstyle=WARNING,
            width=15
        )
        self.update_pkg_btn.grid(row=1, column=0, padx=5, pady=5, sticky="nsew")

        self.list_pkg_btn = ttk.Button(
            button_frame,
            text="查询已安装包",
            command=self.list_packages,
            bootstyle=SECONDARY,
            width=15
        )
        self.list_pkg_btn.grid(row=1, column=1, padx=5, pady=5, sticky="nsew")

        self.python_version_btn = ttk.Button(
            button_frame,
            text="查询Python版本",
            command=self.check_python_version,
            bootstyle=PRIMARY,
            width=15
        )
        self.python_version_btn.grid(row=1, column=2, padx=5, pady=5, sticky="nsew")

        # 底部状态栏 - 使用网格布局确保稳定性
        status_frame = ttk.LabelFrame(main_frame, text="状态", padding=(10, 5))
        status_frame.pack(fill=X, side=BOTTOM, pady=5)

        # 使用网格布局管理器
        status_frame.columnconfigure(0, weight=1)  # 状态标签列
        status_frame.columnconfigure(1, weight=3)  # 进度条列
        status_frame.columnconfigure(2, weight=1)  # 按钮列

        # 状态标签 - 左侧
        self.status_label = ttk.Label(
            status_frame,
            text="就绪",
            font=("Helvetica", 9),
            anchor="w"
        )
        self.status_label.grid(row=0, column=0, padx=(0, 10), sticky="w")

        # 进度条 - 中间（初始隐藏）
        self.progress_bar = ttk.Progressbar(
            status_frame,
            bootstyle=SUCCESS,
            mode='indeterminate',
            length=100
        )
        # 初始不显示进度条
        self.progress_bar.grid_remove()

        # 清除按钮 - 右侧
        self.clear_btn = ttk.Button(
            status_frame,
            text="清除输出",
            command=self.clear_output,
            bootstyle=(OUTLINE, SECONDARY),
            width=10
        )
        self.clear_btn.grid(row=0, column=2, padx=(0, 5), sticky="e")

        # 输出区域
        output_frame = ttk.LabelFrame(main_frame, text="输出信息", padding=10)
        output_frame.pack(fill=BOTH, expand=YES, pady=(0, 15))

        # 创建文本区域和滚动条
        text_frame = ttk.Frame(output_frame)
        text_frame.pack(fill=BOTH, expand=YES)

        self.scrollbar = ttk.Scrollbar(text_frame)
        self.scrollbar.pack(side=RIGHT, fill=Y)

        self.output_text = tk.Text(
            text_frame,
            wrap=tk.WORD,
            yscrollcommand=self.scrollbar.set,
            bg="#2b3e50",  # 匹配superhero主题的背景色
            fg="#ffffff",  # 白色文本
            font=("Consolas", 10),
            padx=5,
            pady=5
        )
        self.output_text.pack(fill=BOTH, expand=YES)
        self.scrollbar.config(command=self.output_text.yview)
        self.output_text.configure(state='disabled')

        # 确保所有控件立即布局
        self.update_idletasks()

    def show_progress_bar(self):
        """显示进度条"""
        if self.progress_bar.winfo_exists():
            self.progress_bar.grid(row=0, column=1, padx=5, sticky="ew")
            self.update_idletasks()

    def hide_progress_bar(self):
        """隐藏进度条"""
        if self.progress_bar.winfo_exists() and self.progress_bar.winfo_ismapped():
            self.progress_bar.stop()
            self.progress_bar.grid_remove()
            self.update_idletasks()

    def find_python_in_path(self):
        """在系统PATH中查找Python解释器"""
        if sys.platform == "win32":
            # Windows系统
            for path in os.environ["PATH"].split(os.pathsep):
                python_exe = os.path.join(path, "python.exe")
                if os.path.isfile(python_exe):
                    return python_exe
        else:
            # macOS/Linux系统
            return shutil.which("python3") or shutil.which("python")
        return None

    def check_python_environment(self):
        """检查Python环境是否可用"""
        # 1. 检查是否在打包环境中
        is_frozen = getattr(sys, 'frozen', False)

        if is_frozen:
            # 打包环境下的特殊处理
            self.update_output("检测到程序在打包环境中运行")

            # 尝试查找系统PATH中的Python
            python_path = self.find_python_in_path()

            if python_path:
                self.python_executable = python_path
                self.python_status_label.config(text=f"检测到Python环境: {python_path}", bootstyle="success")
                self.download_python_btn.config(state=DISABLED)
                self.enable_buttons()
                self.update_output(f"在系统PATH中找到Python解释器: {python_path}")
            else:
                self.python_executable = None
                self.python_status_label.config(text="未检测到Python环境!", bootstyle="danger")
                self.download_python_btn.config(state=NORMAL, bootstyle="warning")
                self.disable_buttons()
                self.update_output("错误: 未检测到Python环境!")
                self.update_output("请确保Python已安装并添加到系统PATH环境变量")
        else:
            # 未打包环境
            if not hasattr(sys, 'executable') or not sys.executable or not os.path.exists(sys.executable):
                self.python_executable = None
                self.python_status_label.config(text="未检测到Python环境!", bootstyle="danger")
                self.download_python_btn.config(state=NORMAL, bootstyle="warning")
                self.disable_buttons()
                self.update_output("错误: 未检测到Python环境!")
                self.update_output("请安装Python或配置环境变量后再使用本程序。")
            else:
                self.python_executable = sys.executable
                self.python_status_label.config(text=f"Python环境正常: {sys.executable}", bootstyle="success")
                self.download_python_btn.config(state=DISABLED)
                self.enable_buttons()
                self.update_output(f"检测到Python环境: {sys.executable}")

    def download_python(self):
        """下载Python安装包"""
        if messagebox.askyesno(
                "下载Python",
                "是否要打开浏览器下载最新稳定版Python?\n\n推荐版本: Python 3.12",
                parent=self
        ):
            webbrowser.open("https://www.python.org/downloads/")
            self.update_output("已打开Python官方下载页面")
            self.update_output("请下载并安装Python后重新启动本程序")

    def run_command(self, command, on_complete=None):
        """在单独的线程中运行命令"""
        if not self.python_executable:
            self.update_output("错误: Python环境不可用!")
            self.check_python_environment()
            return

        # 确保UI更新
        self.update_idletasks()

        # 显示进度条
        self.show_progress_bar()
        self.progress_bar.start(10)
        self.set_status("正在执行...")
        self.disable_buttons()
        self.update_idletasks()  # 强制UI更新

        def execute():
            try:
                self.update_output(f"执行命令: {command}")
                process = subprocess.Popen(
                    command,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    text=True,
                    shell=True,
                    encoding='utf-8',
                    errors='replace'
                )

                # 实时读取输出
                while True:
                    output_line = process.stdout.readline()
                    if output_line == '' and process.poll() is not None:
                        break
                    if output_line:
                        self.update_output(output_line.strip())

                # 获取剩余输出和错误
                stdout, stderr = process.communicate()

                if stdout:
                    self.update_output(stdout)
                if stderr:
                    self.update_output("错误: " + stderr)

                if process.returncode == 0:
                    self.update_output("\n命令执行成功!")
                    self.set_status("操作完成")
                else:
                    self.update_output(f"\n命令执行失败，返回代码: {process.returncode}")
                    self.set_status("操作失败")

                if on_complete:
                    on_complete()
            except Exception as e:
                self.update_output(f"执行出错: {str(e)}")
                self.set_status("执行错误")
            finally:
                # 停止并隐藏进度条
                self.progress_bar.stop()
                self.hide_progress_bar()
                self.enable_buttons()
                # 确保UI更新
                self.update_idletasks()

        thread = threading.Thread(target=execute)
        thread.daemon = True
        thread.start()

    def update_output(self, text):
        """更新输出文本区域"""

        def update():
            try:
                # 检查窗口是否还存在
                if not self.winfo_exists():
                    return
                self.output_text.configure(state='normal')
                self.output_text.insert(tk.END, text + "\n")
                self.output_text.see(tk.END)
                self.output_text.configure(state='disabled')
                # 确保UI更新
                self.update_idletasks()
            except tk.TclError as e:
                print(f"更新输出时出错: {e}")

        self.after(0, update)

    def set_status(self, text):
        """更新状态标签"""

        def update():
            try:
                if not self.winfo_exists():
                    return
                self.status_label.config(text=text)
                # 确保UI更新
                self.update_idletasks()
            except tk.TclError as e:
                print(f"更新状态时出错: {e}")

        self.after(0, update)

    def disable_buttons(self):
        """禁用所有操作按钮"""

        def update():
            try:
                if not self.winfo_exists():
                    return
                for btn in [self.install_btn, self.uninstall_btn, self.update_pip_btn,
                            self.update_pkg_btn, self.list_pkg_btn, self.python_version_btn]:
                    if btn.winfo_exists():  # 确保按钮还存在
                        btn.config(state=DISABLED)
                # 确保UI更新
                self.update_idletasks()
            except tk.TclError as e:
                print(f"禁用按钮时出错: {e}")

        self.after(0, update)

    def enable_buttons(self):
        """启用所有操作按钮"""

        def update():
            try:
                if not self.winfo_exists():
                    return
                for btn in [self.install_btn, self.uninstall_btn, self.update_pip_btn,
                            self.update_pkg_btn, self.list_pkg_btn, self.python_version_btn]:
                    if btn.winfo_exists():  # 确保按钮还存在
                        btn.config(state=NORMAL)
                # 确保UI更新
                self.update_idletasks()
            except tk.TclError as e:
                print(f"启用按钮时出错: {e}")

        self.after(0, update)

    def clear_output(self):
        """清除输出区域"""

        def update():
            try:
                if not self.winfo_exists():
                    return
                self.output_text.configure(state='normal')
                self.output_text.delete(1.0, tk.END)
                self.output_text.configure(state='disabled')
                self.set_status("就绪")
                self.update_output("输出已清除")
                # 确保UI更新
                self.update_idletasks()
            except tk.TclError as e:
                print(f"清除输出时出错: {e}")

        self.after(0, update)

    def install_package(self):
        """安装包"""
        packages = self.package_entry.get().strip()
        if not packages:
            self.update_output("请输入要安装的包名称")
            return

        self.update_output(f"正在安装包: {packages}")
        command = f'"{self.python_executable}" -m pip install {packages}'
        self.run_command(command)

    def uninstall_package(self):
        """卸载包"""
        packages = self.package_entry.get().strip()
        if not packages:
            self.update_output("请输入要卸载的包名称")
            return

        self.update_output(f"正在卸载包: {packages}")
        command = f'"{self.python_executable}" -m pip uninstall -y {packages}'
        self.run_command(command)

    def update_pip(self):
        """更新pip"""
        self.update_output("正在更新pip...")
        command = f'"{self.python_executable}" -m pip install --upgrade pip'
        self.run_command(command)

    def update_package(self):
        """更新包"""
        packages = self.package_entry.get().strip()
        if not packages:
            self.update_output("请输入要更新的包名称")
            return

        self.update_output(f"正在更新包: {packages}")
        command = f'"{self.python_executable}" -m pip install --upgrade {packages}'
        self.run_command(command)

    def list_packages(self):
        """列出已安装的包"""
        self.update_output("正在获取已安装的包列表...")
        command = f'"{self.python_executable}" -m pip list'
        self.run_command(command)

    def check_python_version(self):
        """检查Python版本"""
        self.update_output("正在获取Python版本信息...")
        command = f'"{self.python_executable}" --version'
        self.run_command(command)

        # 额外显示pip版本
        command2 = f'"{self.python_executable}" -m pip --version'
        self.run_command(command2)


def main():
    try:
        app = PipManager()
        app.mainloop()
    except Exception as e:
        # 捕获并显示未处理的异常
        import traceback
        error_msg = f"程序发生严重错误: {str(e)}\n\n{traceback.format_exc()}\n请截图此信息并联系开发者。"
        messagebox.showerror("严重错误", error_msg)
        with open("pip_manager_error.log", "w", encoding="utf-8") as f:
            f.write(error_msg)


if __name__ == "__main__":
    main()